//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web.Compilation;
using System.Web.UI;
using DreamDriven.Components.DataSource.EventsArgs;
using DreamDriven.Components.DataSource.Utility;
using DreamDriven.SoftwareFactory.Components.Properties;

namespace DreamDriven.Components.DataSource
{
	/// <summary>
	/// Defnies a data source view to be used with the <see cref="DomainObjectDataSource"/> control.
	/// </summary>
	public class DomainObjectDataSourceView : DataSourceView
	{
		#region Private Fields

		private readonly List<object> _data = new List<object>();
        private readonly DomainObjectDataSource _owner;
        private bool _usingServerSorting;
		private string _dataObjectTypeName;
		private Type _dataObjectType;
		private int _totalRowCount = -1;
		private bool _usingServerPaging;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of <see cref="DomainObjectDataSourceView"/>.
		/// </summary>
		/// <param name="owner">The <see cref="DomainObjectDataSource"/> owner of this view.</param>
		/// <param name="name">The name of this view.</param>
		public DomainObjectDataSourceView(DomainObjectDataSource owner, string name)
			: base(owner, name)
		{
            _owner = owner;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets a <see cref="ReadOnlyCollection{T}"/> with the data maanged by this view.
		/// </summary>
		public virtual ReadOnlyCollection<object> Items
		{
			get { return Data.AsReadOnly(); }
		}

		/// <summary>
		/// Sets the data source for this view.
		/// </summary>
        [SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly")]
        public virtual object DataSource
		{
			set
			{
				Data.Clear();

                if (value == null)
                {
                    return;
                }

			    var values = value as IEnumerable;
				if (values != null)
				{
					SafeAddRange(values);
				}
				else
				{
					SafeAdd(value);
				}
				OnDataSourceViewChanged(EventArgs.Empty);
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports the <see cref="DataSourceView.ExecuteDelete(System.Collections.IDictionary,System.Collections.IDictionary)"/>
		/// operation.
		/// </summary>
		public override bool CanDelete
		{
			get { return true; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports the <see cref="DataSourceView.ExecuteUpdate(System.Collections.IDictionary,System.Collections.IDictionary,System.Collections.IDictionary)"/>
		/// operation.
		/// </summary>
		public override bool CanUpdate
		{
			get { return true; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports the <see cref="DataSourceView.ExecuteInsert(System.Collections.IDictionary)"/>
		/// operation.
		/// </summary>
		public override bool CanInsert
		{
			get { return true; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports retrieving
		/// the total number of data rows, instead of the data.
		/// </summary>
		public override bool CanRetrieveTotalRowCount
		{
			get { return true; }
		}

		/// <summary>
		///  Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports a sorted
		/// view on the underlying data source.
		/// </summary>

		public override bool CanSort
		{
			get { return true; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object supports paging through
		/// the data retrieved by the <see cref="DataSourceView.ExecuteSelect(System.Web.UI.DataSourceSelectArguments)"/>
		/// method.
		/// </summary>
		public override bool CanPage
		{
			get { return true; }
		}

		/// <summary>
		/// Gets or sets a value indicating if the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object uses sorting on the server.
		/// </summary>
		public virtual bool UsingServerSorting
		{
			get { return _usingServerSorting; }
			set
			{
				if (UsingServerSorting != value)
				{
					_usingServerSorting = value;
					OnDataSourceViewChanged(EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Gets or sets a value indicating if the <see cref="DataSourceView"/> object associated
		/// with the current <see cref="DataSourceControl"/> object uses paging on the server.
		/// </summary>
		public virtual bool UsingServerPaging
		{
			get { return _usingServerPaging; }
			set
			{
				if (UsingServerPaging != value)
				{
					_usingServerPaging = value;
					OnDataSourceViewChanged(EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Gets or sets the total number of rows retrieved in a data retrieval operation.
		/// </summary>
		public virtual int TotalRowCount
		{
			protected get { return _totalRowCount; }
			set
			{
				ArgumentValidator.ArgumentNonNegative(value, "value");

				if (TotalRowCount != value)
				{
					_totalRowCount = value;
					OnDataSourceViewChanged(EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Gets or sets the data type name of the data objects.
		/// </summary>
		public virtual string DataObjectTypeName
		{
			get
			{
				return _dataObjectTypeName ?? String.Empty;
			}
			set
			{
				if (_dataObjectTypeName != value)
				{
					_dataObjectTypeName = value;
					OnDataSourceViewChanged(EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Gets the list of data objects managed by this view.
		/// </summary>
        [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
        protected virtual List<object> Data
		{
			get { return _data; }
		}

        /// <summary>
        /// Gets the <see cref="DomainObjectDataSource"/> owner of this view.
        /// </summary>
        public virtual DomainObjectDataSource Owner
        {
            get { return _owner; }
        }


		#endregion

		#region Events

		private readonly static object DeletedEventKey = new object();
		private readonly static object InsertedEventKey = new object();
		private readonly static object UpdatedEventKey = new object();
		private readonly static object DeletingEventKey = new object();
		private readonly static object InsertingEventKey = new object();
		private readonly static object UpdatingEventKey = new object();
		private readonly static object SelectingEventKey = new object();

		/// <summary>
		/// Occurs after a delete operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceStatusEventArgs> Deleted
		{
			add { Events.AddHandler(DeletedEventKey, value); }
			remove { Events.RemoveHandler(DeletedEventKey, value); }
		}

		/// <summary>
		/// Occurs after an update operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceStatusEventArgs> Updated
		{
			add { Events.AddHandler(UpdatedEventKey, value); }
			remove { Events.RemoveHandler(UpdatedEventKey, value); }
		}

		/// <summary>
		/// Occurs after an insert operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceStatusEventArgs> Inserted
		{
			add { Events.AddHandler(InsertedEventKey, value); }
			remove { Events.RemoveHandler(InsertedEventKey, value); }
		}

		/// <summary>
		/// Cancelable event that occurs before a delete operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceDeletingEventArgs> Deleting
		{
			add { Events.AddHandler(DeletingEventKey, value); }
			remove { Events.RemoveHandler(DeletingEventKey, value); }
		}

		/// <summary>
		/// Cancelable event that occurs before an update operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceUpdatingEventArgs> Updating
		{
			add { Events.AddHandler(UpdatingEventKey, value); }
			remove { Events.RemoveHandler(UpdatingEventKey, value); }
		}

		/// <summary>
		/// Cancelable event that occurs before an insert operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceInsertingEventArgs> Inserting
		{
			add { Events.AddHandler(InsertingEventKey, value); }
			remove { Events.RemoveHandler(InsertingEventKey, value); }
		}

		/// <summary>
		/// Cancelabe event that occurs before a select operation.
		/// </summary>
		public event EventHandler<DomainObjectDataSourceSelectingEventArgs> Selecting
		{
			add { Events.AddHandler(SelectingEventKey, value); }
			remove { Events.RemoveHandler(SelectingEventKey, value); }
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Deleted"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnDeleted(DomainObjectDataSourceStatusEventArgs e)
		{
			var handler =
				Events[DeletedEventKey] as EventHandler<DomainObjectDataSourceStatusEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Updated"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnUpdated(DomainObjectDataSourceStatusEventArgs e)
		{
		    var handler =
		        Events[UpdatedEventKey] as EventHandler<DomainObjectDataSourceStatusEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Inserted"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnInserted(DomainObjectDataSourceStatusEventArgs e)
		{
			var handler =
				Events[InsertedEventKey] as EventHandler<DomainObjectDataSourceStatusEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Deleting"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnDeleting(DomainObjectDataSourceDeletingEventArgs e)
		{
			var handler =
				Events[DeletingEventKey] as EventHandler<DomainObjectDataSourceDeletingEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Inserting"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnInserting(DomainObjectDataSourceInsertingEventArgs e)
		{
			var handler =
				Events[InsertingEventKey] as EventHandler<DomainObjectDataSourceInsertingEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Updating"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual void OnUpdating(DomainObjectDataSourceUpdatingEventArgs e)
		{
			var handler =
				Events[UpdatingEventKey] as EventHandler<DomainObjectDataSourceUpdatingEventArgs>;

			if (handler != null)
			{
				handler(this, e);
			}
		}

		/// <summary>
		/// Fires the <see cref="DomainObjectDataSourceView.Selecting"/> event.
		/// </summary>
		/// <param name="e">The event associated data.</param>
		protected virtual bool OnSelecting(DomainObjectDataSourceSelectingEventArgs e)
		{
			var handler =
				Events[SelectingEventKey] as EventHandler<DomainObjectDataSourceSelectingEventArgs>;

			if (handler != null)
			{
				handler(this, e);
				return true;
			}
		    return false;
		}

		#endregion

        #region Action Methods

        /// <summary>
		/// Performs an asynchronous insert operation on the list of data that the <see	cref="DataSourceView"/> object represents.
		/// </summary>
		/// <param name="values">An <see cref="IDictionary"/> of name/value pairs used during an insert operation.</param>
		/// <param name="callback"></param>
		public override void Insert(IDictionary values, DataSourceViewOperationCallback callback)
		{
			ThrowIfNoDefaultConstructor(GetDataObjectType());
			base.Insert(values, callback);
		}

		/// <summary>
		/// Gets a list of data from the underlying data storage.
		/// </summary>
		/// <param name="arguments">A <see cref="DataSourceSelectArguments"/> that is used to request operations on the data 
		/// beyond basic data retrieval.</param>
		/// <returns>An <see cref="IEnumerable"/> list of data from the underlying data storage.</returns>
		protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
		{
            AddSupportedCapabilities(arguments);
			
			var selectingEventArgs = new DomainObjectDataSourceSelectingEventArgs(arguments);
            FireSelectingEvent(selectingEventArgs);
			if (selectingEventArgs.Cancel)
				return null;

            SortDataForSelectIfNeeded(arguments);

			if (arguments.RetrieveTotalRowCount)
			{
				arguments.TotalRowCount = _totalRowCount < 0 ? Data.Count : _totalRowCount;
			}
			if (arguments.MaximumRows > 0)
			{
				if (UsingServerPaging) 
				{
                    ValidateTotalRowCountNotNegative(TotalRowCount);
				}
				else
				{
                    ValidateStartRowIndexNotNegative(arguments);

                    if ((Data.Count == 0 && arguments.StartRowIndex > 0) 
                        || (Data.Count > 0 && (arguments.StartRowIndex >= Data.Count)))
                    {
                        string message = String.Format(CultureInfo.CurrentCulture,
                                                       Resources.StartRowIndexOutOfRange,
                                                       arguments.StartRowIndex, Data.Count);
                        throw new ArgumentOutOfRangeException(message);
                    }

				    int rowsLeft = Data.Count - arguments.StartRowIndex;
					int rowCount = Math.Min(rowsLeft,arguments.MaximumRows);
					return Data.GetRange(arguments.StartRowIndex, rowCount);
				}
			}

			return Data;
		}

		/// <summary>
		/// Performs an insert operation on the list of data that the <see cref="DataSourceView"/> object represents.
		/// </summary>
		/// <param name="values">An <see cref="IDictionary"/> of name/value pairs used during an insert operation.</param>
		/// <returns>The number of items that were inserted into the underlying data storage.</returns>
		protected override int ExecuteInsert(IDictionary values)
		{
			ArgumentValidator.ArgumentNotNull(values, "values");

			var insertingEventArgs = new DomainObjectDataSourceInsertingEventArgs(values);
			OnInserting(insertingEventArgs);
			if (insertingEventArgs.Cancel)
				return 0;

			object instance = CreateInstance();
			BuildInstance(values, instance);
			Add(instance);
			OnDataSourceViewChanged(EventArgs.Empty);

			int rowsAffected = 1;
			var insertedEventArgs = new DomainObjectDataSourceStatusEventArgs(instance, rowsAffected);
			OnInserted(insertedEventArgs);

			return rowsAffected;
		}

		/// <summary>
		/// Performs a delete operation on the list of data that the <see cref="DataSourceView"/> object represents.
		/// </summary>
		/// <param name="keys">An <see cref="IDictionary"/> of object or row keys to be deleted by
		/// the <see cref="DataSourceView.ExecuteDelete(System.Collections.IDictionary,System.Collections.IDictionary)"/>
		/// operation.</param>
		/// <param name="oldValues">An <see cref="System.Collections.IDictionary"/> of name/value pairs that represent data elements and their original values.</param>
		/// <returns>The number of items that were deleted from the underlying data storage.</returns>
		protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
		{
			ArgumentValidator.CollectionNotNullNorEmpty(keys, String.Format(CultureInfo.CurrentCulture, Resources.NoKeysSpecified), "keys");

			var deletingEventArgs = new DomainObjectDataSourceDeletingEventArgs(GetReadOnlyDictionary(keys), oldValues);
			OnDeleting(deletingEventArgs);
            if (deletingEventArgs.Cancel)
            {
                return 0;
            }

		    int rowsAffected;
			object instance = FindInstance(keys);
			if (instance == null)
			{
				rowsAffected = 0;
			}
			else
			{
				Data.Remove(instance);
				rowsAffected = 1;
			}
			instance = CreateInstance();
			BuildInstance(oldValues, instance);
			BuildInstance(keys, instance);
			OnDataSourceViewChanged(EventArgs.Empty);

			var deletedEventArgs = new DomainObjectDataSourceStatusEventArgs(instance, rowsAffected);
			OnDeleted(deletedEventArgs);

			return rowsAffected;
		}

		/// <summary>
		/// Performs an update operation on the list of data that the <see cref="DataSourceView"/> object represents.
		/// </summary>
		/// <param name="keys">An <see cref="System.Collections.IDictionary"/> of object or row keys to be updated by the update operation.</param>
		/// <param name="values">An <see cref="System.Collections.IDictionary"/> of name/value pairs that represent data elements and their new values.</param>
		/// <param name="oldValues">An <see cref="System.Collections.IDictionary"/> of name/value pairs that represent data elements and their original values.</param>
		/// <returns>The number of items that were updated in the underlying data storage.</returns>
		protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
		{
		    string message = String.Format(CultureInfo.CurrentCulture, Resources.NoKeysSpecified);
			ArgumentValidator.CollectionNotNullNorEmpty(keys, message, "keys");
			ArgumentValidator.ArgumentNotNull(values, "values");

			var updatingEventArgs = new DomainObjectDataSourceUpdatingEventArgs(GetReadOnlyDictionary(keys), values, oldValues);
			OnUpdating(updatingEventArgs);
			if (updatingEventArgs.Cancel)
				return 0;

			object newInstance = CreateInstance();
			BuildInstance(keys, newInstance);
			BuildInstance(values, newInstance);
			int rowsAffected;
			object oldInstance = FindInstance(keys);
			if (oldInstance != null)
			{
				int index = Data.IndexOf(oldInstance);
				Data[index] = newInstance;
				rowsAffected = 1;
			}
			else
			{
				rowsAffected = 0;
			}
			OnDataSourceViewChanged(EventArgs.Empty);

			var updatedEventArgs = new DomainObjectDataSourceStatusEventArgs(newInstance, rowsAffected);
			OnUpdated(updatedEventArgs);

			return rowsAffected;
		}

        #endregion Action Methods

        #region Aux Methods

        private void SafeAdd(object item)
        {
            ThrowIfInvalidType(item, GetDataObjectType());

            Data.Add(item);
        }

        private void SafeAddRange(IEnumerable items)
        {
            foreach (object item in items)
            {
                SafeAdd(item);
            }
        }

        private void Add(object item)
        {
            Data.Add(item);
        }

        private Type GetDataObjectType()
		{
			if (_dataObjectType == null)
			{
                if (String.IsNullOrEmpty(DataObjectTypeName))
                {
                    string message = String.Format(CultureInfo.CurrentCulture, Resources.DataObjectTypeNameNotSet);
                    throw new InvalidOperationException(message);
                }

			    Type type = BuildManager.GetType(DataObjectTypeName, false, true);
                if (type == null)
                {
                    string message = String.Format(CultureInfo.CurrentCulture,
                                                   Resources.DataObjectTypeNotFound,
                                                   DataObjectTypeName);
                    throw new InvalidOperationException(message);
                }

			    _dataObjectType = type;
			}
			return _dataObjectType;
		}

		private object CreateInstance()
		{
			Type type = GetDataObjectType();
			return Activator.CreateInstance(type);
		}

		private object FindInstance(IDictionary keys)
		{
			return _data.Find(delegate(object obj)
			{
				PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(obj);
				foreach (string keyName in keys.Keys)
				{
					PropertyDescriptor property = GetValidProperty(properties, keyName);
                    if (!property.GetValue(obj).Equals(keys[keyName]))
                    {
                        return false;
                    }
				}
				return true;
			});
		}

        private static void ValidateTotalRowCountNotNegative(int totalRowCount)
        {
            if (totalRowCount < 0)
            {
                string message = String.Format(CultureInfo.CurrentCulture,
                                               Resources.InvalidServerPagingSettings);
                throw new InvalidOperationException(message);
            }
        }

        private static void ValidateStartRowIndexNotNegative(DataSourceSelectArguments arguments)
        {
            if (arguments.StartRowIndex < 0)
            {
                string message = String.Format(CultureInfo.CurrentCulture, Resources.InvalidStartRowIndex,
                                               arguments.StartRowIndex);
                throw new ArgumentException(message);
            }
        }

        private void FireSelectingEvent(DomainObjectDataSourceSelectingEventArgs selectingEventArgs)
        {
            bool handled = OnSelecting(selectingEventArgs);
            if (!handled && UsingServerPaging)
            {
                string message = String.Format(CultureInfo.CurrentCulture,
                                               Resources.UsingServerPagingAndSelectingEventNotHandled);
                throw new InvalidOperationException(message);
            }
        }

        private void SortDataForSelectIfNeeded(DataSourceSelectArguments arguments)
        {
            if (!(UsingServerSorting || String.IsNullOrEmpty(arguments.SortExpression)))
            {
                Data.Sort(new ObjectComparer(arguments.SortExpression, GetDataObjectType()));
            }
        }

        private void AddSupportedCapabilities(DataSourceSelectArguments arguments)
        {
            if (CanSort)
            {
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Sort);
            }
            if (CanRetrieveTotalRowCount)
            {
                arguments.AddSupportedCapabilities(DataSourceCapabilities.RetrieveTotalRowCount);
            }
            if (CanPage)
            {
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Page);
            }

            arguments.RaiseUnsupportedCapabilitiesError(this);
        }



        #endregion Aux Methods

        #region Nested Types

        private class ObjectComparer : IComparer<object>
		{
		    private readonly PropertyDescriptor _property;
			private readonly bool _desc;

			public ObjectComparer(string sortExpression, Type type)
			{
				string[] sortExpressionParts = sortExpression.Split(' ');

				PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(type);
				_property = properties.Find(sortExpressionParts[0], true);
                if (_property == null)
                {
                    string message = String.Format(CultureInfo.CurrentCulture,
                                                   Resources.InvalidPropertyInSortingExpression,
                                                   sortExpressionParts[0]);
                    throw new ArgumentException(message);
                }

                if (!typeof(IComparable).IsAssignableFrom(_property.PropertyType))
                {
                    string message = String.Format(CultureInfo.CurrentCulture,
                                                   Resources.PropertyDoesNotImplementIComparable,
                                                   sortExpressionParts[0]);
                    throw new InvalidOperationException(message);
                }

			    if (sortExpressionParts.Length > 1)
				{
					string order = sortExpressionParts[1];
					if (order.Equals("DESC"))
					{
						_desc = true;
					}
                    else if (!order.Equals("ASC"))
                    {
                        string message = String.Format(CultureInfo.CurrentCulture,
                                                       Resources.InvalidSortingExpression,
                                                       sortExpressionParts[1]);
                        throw new ArgumentException(message);
                    }
				}
			}

			#region IComparer<object> Members

			public int Compare(object x, object y)
			{
			    var valueX = (IComparable) _property.GetValue(x);
			    var valueY = (IComparable) _property.GetValue(y);
				int comparisonResult = valueX.CompareTo(valueY);
				if (_desc)
				{
					comparisonResult *= -1;
				}
				return comparisonResult;
            }

            #endregion IComparer<object> Members
        }

		#endregion Nested Types

        #region Helpers

        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by ObjectValidator class.")]
        private static IDictionary GetReadOnlyDictionary(IDictionary dictionary)
        {
            ArgumentValidator.ArgumentNotNull(dictionary, "dictionary");

            var result = new OrderedDictionary();
            foreach (DictionaryEntry entry in dictionary)
            {
                result.Add(entry.Key, entry.Value);
            }
            return result.AsReadOnly();
        }

        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        private static PropertyDescriptor GetValidProperty(PropertyDescriptorCollection properties, string propertyName)
        {
            ArgumentValidator.ArgumentNotNull(properties, "properties");
            PropertyDescriptor property = properties.Find(propertyName, false);
            ArgumentValidator.PropertyNotNull(property, propertyName);
            return property;
        }



        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        private static void BuildInstance(IDictionary values, object existing)
        {
            ArgumentValidator.ArgumentNotNull(values, "values");
            ArgumentValidator.ArgumentNotNull(existing, "existing");

            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(existing);
            foreach (DictionaryEntry entry in values)
            {
                string propertyName = entry.Key.ToString();
                PropertyDescriptor property = GetValidProperty(properties, propertyName);
                if (property.IsReadOnly)
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.PropertyIsReadOnly, propertyName));

                object value = GetObjectValue(entry.Value, property.PropertyType, propertyName);
                property.SetValue(existing, value);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        private static void ThrowIfInvalidType(object item, Type expectedType)
        {
            ArgumentValidator.ArgumentNotNull(item, "item");
            ArgumentValidator.ArgumentNotNull(expectedType, "expectedType");

            if (!expectedType.IsAssignableFrom(item.GetType()))
            {
                string message = String.Format(CultureInfo.CurrentCulture,
                                               Resources.InvalidObjectType, expectedType.FullName,
                                               item.GetType().FullName);
                throw new InvalidOperationException(message);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        private static void ThrowIfNoDefaultConstructor(Type type)
        {
            ArgumentValidator.ArgumentNotNull(type, "type");

            if (type.GetConstructor(Type.EmptyTypes) == null)
            {
                string message = String.Format(CultureInfo.CurrentCulture,
                                               Resources.DefaultConstructorMissing, type.FullName);
                throw new InvalidOperationException(message);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        private static object GetObjectValue(object value, Type targetType, string paramName)
        {
            ArgumentValidator.ArgumentNotNull(targetType, "targetType");

            if ((value == null) || targetType.IsInstanceOfType(value))
            {
                return value;
            }

            value = TransformType(value, targetType, paramName);
            return value;
        }

        private static object TransformType(object value, Type targetType, string paramName)
        {
            ArgumentValidator.ArgumentNotNull(targetType, "targetType");

            string stringValue = (string) value;
            if (stringValue == null)
                return value;

            TypeConverter converter = TypeDescriptor.GetConverter(targetType);
            try
            {
                value = converter.ConvertFromString(stringValue);
            }
            catch
            {
                string message = String.Format(CultureInfo.CurrentCulture, Resources.CannotConvertType, paramName,
                                               typeof(string).FullName, targetType.FullName);
                throw new InvalidOperationException(message);
            }
            return value;
        }

        #endregion Helpers
    }
}
